﻿/**
 * Copyright (C) 2012 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Collections;
using NGuice.Inject.Internal.Util;
using NGuice.Inject.Spi;

namespace NGuice.Inject.Internal
{
    internal sealed class InheritingState : IState
    {
        private readonly IState parent;

        // Must be a linked hashmap in order to preserve order of bindings in Modules.
        private readonly IDictionary explicitBindingsMutable = new ListDictionary();//key为Key，value为Binding
        private readonly IDictionary explicitBindings;
        private readonly IDictionary<Type, IScope> scopes = new Dictionary<Type, IScope>();
        private readonly IList<TypeConverterBinding> converters = new List<TypeConverterBinding>();
        /*if[AOP]*/
        private readonly IList<MethodAspect> methodAspects = new List<MethodAspect>();
        /*end[AOP]*/
        private readonly IList<TypeListenerBinding> listenerBindings = new List<TypeListenerBinding>();
        private readonly WeakKeySet blacklistedKeys = new WeakKeySet();
        private readonly object locked;

        public InheritingState(IState parent)
        {
            this.parent = Preconditions.CheckNotNull(parent, "parent");
            this.locked = (parent == NoneState.INSTANCE) ? this : parent.Lock();
            explicitBindings = new ReadOnlyDictionary(explicitBindingsMutable);
        }

        public IState Parent
        {
            get { return this.parent; }
        }

        public BindingImpl<T> GetExplicitBinding<T>(Key<T> key)
        {
            IBinding<T> binding = explicitBindings.Contains(key) ? explicitBindings[key] as IBinding<T> : null;
            return binding != null ? (BindingImpl<T>)binding : parent.GetExplicitBinding(key);
        }

        /// <summary>
        /// 非泛型方法！
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public IBindingImpl GetExplicitBinding(Key key)
        {
            IBinding binding =  explicitBindings.Contains(key) ? explicitBindings[key] as IBinding : null;
            return binding != null ? (IBindingImpl)binding : parent.GetExplicitBinding(key);
        }

        public IDictionary<Key<T>, IBinding<T>> GetExplicitBindingsThisLevel<T>()
        {
            IDictionary<Key<T>, IBinding<T>> bindings = new Dictionary<Key<T>, IBinding<T>>();
            foreach (object key in explicitBindings.Keys)
            {
                if (key.GetType() == typeof(Key<T>))
                {
                    bindings.Add((key as Key<T>), (explicitBindings[key] as IBinding<T>));
                }
            }
            return bindings;
        }

        /// <summary>
        /// 非泛型方法！
        /// </summary>
        /// <returns></returns>
        public IDictionary<Key, IBinding> GetExplicitBindingsThisLevel()
        {
            IDictionary<Key, IBinding> bindings = new Dictionary<Key, IBinding>();
            foreach (object key in explicitBindings.Keys)
            {
                if (typeof(Key).IsAssignableFrom(key.GetType()))
                {
                    bindings.Add((key as Key), (explicitBindings[key] as IBinding));
                }
            }
            return bindings;
        }

        public void PutBinding<T>(Key<T> key, BindingImpl<T> binding)
        {
            if (explicitBindingsMutable.Contains(key))
                explicitBindingsMutable[key] = binding;
            else
                explicitBindingsMutable.Add(key, binding);
        }

        /// <summary>
        /// 非泛型方法！
        /// </summary>
        /// <param name="key"></param>
        /// <param name="binding"></param>
        public void PutBinding(Key key, IBindingImpl binding)
        {
            if (explicitBindingsMutable.Contains(key))
                explicitBindingsMutable[key] = binding;
            else
                explicitBindingsMutable.Add(key, binding);
        }

        public IScope GetScope(Type scopingAnnotation)
        {
            IScope scope =  scopes.ContainsKey(scopingAnnotation) ? scopes[scopingAnnotation] : null;
            return scope != null ? scope : parent.GetScope(scopingAnnotation);
        }

        public IScope GetScope<T>() where T : Attribute
        {
            return GetScope(typeof(T));
        }

        public void PutAnnotation(Type annotationType, IScope scope)
        {
            this.scopes.Add(annotationType, scope);
        }

        public void PutAnnotation<T>(IScope scope) where T : Attribute
        {
            PutAnnotation(typeof(T), scope);
        }

        public void AddConverter(Spi.TypeConverterBinding typeConverterBinding)
        {
            this.converters.Add(typeConverterBinding);
        }

        public Spi.TypeConverterBinding GetConverter<T>(string stringValue, Errors errors, object source)
        {
            TypeConverterBinding matchingConverter = null;
            for (IState s = this; s != NoneState.INSTANCE; s = s.Parent)
            {
                foreach (TypeConverterBinding converter in s.GetConvertersThisLevel())
                {
                    if (converter.TypeMatcher.Matches(typeof(T)))
                    {
                        if (matchingConverter != null)
                        {
                            errors.AmbiguousTypeConversion(stringValue, source, typeof(T), matchingConverter, converter);
                        }
                        matchingConverter = converter;
                    }
                }
            }
            return matchingConverter;
        }

        public IEnumerable<Spi.TypeConverterBinding> GetConvertersThisLevel()
        {
            return this.converters;
        }

        public void AddMethodAspect(MethodAspect methodAspect)
        {
            methodAspects.Add(methodAspect);
        }

        public IList<MethodAspect> MethodAspects
        {
            get
            {
                return new ImmutableListBuilder<MethodAspect>()
                                .AddAll(parent.MethodAspects)
                                .AddAll(methodAspects)
                                .Build();
            }
        }

        public void AddTypeListener(Spi.TypeListenerBinding typeListenerBinding)
        {
            listenerBindings.Add(typeListenerBinding);
        }

        public IList<TypeListenerBinding> TypeListenerBindings
        {
            get
            {
                List<TypeListenerBinding> list = new List<TypeListenerBinding>();
                list.AddRange(parent.TypeListenerBindings);
                list.AddRange(this.listenerBindings);
                return list;
            }
        }

        public void Blacklist<T>(Key<T> key)
        {
            parent.Blacklist(key);
            blacklistedKeys.Add(key);
        }

        public bool IsBlacklisted<T>(Key<T> key)
        {
            return blacklistedKeys.Contains(key);
        }

        public object Lock()
        {
            return this.locked;
        }

        public IDictionary<Type, IScope> Scopes
        {
            get
            {
                return this.scopes;
            }
        }

    }
}
